using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Threading;
using System.Windows.Forms;
using IMAPI2.Interop;

namespace Odb_TestingProject
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    /// <summary>
    /// Burns data files to disc in a single session using files from a 
    /// single directory tree.
    /// </summary>
    /// <param name="recorder">Burning Device. Must be initialized.</param>
    /// <param name="path">Directory of files to burn.</param>
    public void BurnDirectory(IDiscRecorder2 recorder, String path)
    {
      IDiscFormat2Data dataWriterImage = new MsftDiscFormat2Data();
      dataWriterImage.Recorder = recorder;
      Debug.WriteLine(recorder.DeviceCanLoadMedia);
      if (!dataWriterImage.IsRecorderSupported(recorder))
      {
        Debug.WriteLine("The recorder is not supported");
        return;
      }
      if (!dataWriterImage.IsCurrentMediaSupported(recorder))
      {
        Debug.WriteLine("The current media is not supported");
        return;
      }
      dataWriterImage.ClientName = "IMAPI Sample";
      IFileSystemImage fsi = new MsftFileSystemImage();
      IMAPI_FORMAT2_DATA_MEDIA_STATE state =  (IMAPI_FORMAT2_DATA_MEDIA_STATE)dataWriterImage.CurrentMediaStatus;
      Debug.WriteLine(state);
      if(!dataWriterImage.MediaPhysicallyBlank)
      {
        Debug.WriteLine("burning aborted");
        return;
      }
      
        fsi.FreeMediaBlocks = dataWriterImage.FreeSectorsOnMedia;
      
      fsi.FileSystemsToCreate = FsiFileSystems.FsiFileSystemISO9660;
      fsi.ChooseImageDefaultsForMediaType(IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHRW);
      IFsiDirectoryItem dir = fsi.Root;
      dir.AddTree(path, false);
      Debug.WriteLine("Writing content to disc...");
      IFileSystemImageResult result = fsi.CreateResultImage();
      IStream stream = result.ImageStream;
      DiscFormat2Data_Events progress = dataWriterImage as DiscFormat2Data_Events;
      if (progress == null)
      {
        return;
      }
      progress.Update += new DiscFormat2Data_EventsHandler(DiscFormat2Data_ProgressUpdate);
      dataWriterImage.ForceOverwrite = true;
      dataWriterImage.ForceMediaToBeClosed = true;
      dataWriterImage.Write(stream);
      progress.Update -= new DiscFormat2Data_EventsHandler(DiscFormat2Data_ProgressUpdate);
      Debug.WriteLine("----- Finished writing content -----");
    }

    public void DisplayRecorderInformation(IDiscRecorder2 recorder)
    {
      Debug.WriteLine("--------------------------------------------------------------------------------");
      Debug.WriteLine("ActiveRecorderId: ".PadLeft(22) + recorder.ActiveDiscRecorder);
      Debug.WriteLine("Vendor Id: ".PadLeft(22) + recorder.VendorId);
      Debug.WriteLine("Product Id: ".PadLeft(22) + recorder.ProductId);
      Debug.WriteLine("Product Revision: +".PadLeft(22), recorder.ProductRevision);
      Debug.WriteLine("VolumeName: ".PadLeft(22) + recorder.VolumeName);
      Debug.WriteLine("Can Load Media: ".PadLeft(22) + recorder.DeviceCanLoadMedia);
      Debug.WriteLine("Device Number: ".PadLeft(22) + recorder.LegacyDeviceNumber);
      foreach (String mountPoint in recorder.VolumePathNames)
      {
        Debug.WriteLine("Mount Point: ".PadLeft(22) + mountPoint);
      }
      foreach (IMAPI_FEATURE_PAGE_TYPE supportedFeature in recorder.SupportedFeaturePages)
      {
        Debug.WriteLine("Feature: ".PadLeft(22) + supportedFeature.ToString("F"));
      }
      Debug.WriteLine("Current Features");
      foreach (IMAPI_FEATURE_PAGE_TYPE currentFeature in recorder.CurrentFeaturePages)
      {
        Debug.WriteLine("Feature: ".PadLeft(22) + currentFeature.ToString("F"));
      }
      Debug.WriteLine("Supported Profiles");
      foreach (IMAPI_PROFILE_TYPE supportedProfile in recorder.SupportedProfiles)
      {
        Debug.WriteLine("Profile: ".PadLeft(22) + supportedProfile.ToString("F"));
      }
      Debug.WriteLine("Current Profiles");
      foreach (IMAPI_PROFILE_TYPE currentProfile in recorder.CurrentProfiles)
      {
        Debug.WriteLine("Profile: ".PadLeft(22) + currentProfile.ToString("F"));
      }
      Debug.WriteLine("Supported Mode Pages");
      foreach (IMAPI_MODE_PAGE_TYPE supportedModePage in recorder.SupportedModePages)
      {
        Debug.WriteLine("Mode Page: ".PadLeft(22) + supportedModePage.ToString("F"));
      }
      Debug.WriteLine("\n----- Finished content -----");
    }

    public void DisplayMediaInformation(IDiscRecorder2 recorder)
    {
      IDiscFormat2Data mediaImage = new MsftDiscFormat2Data();
      mediaImage.Recorder = recorder;
      bool boolResult = mediaImage.IsRecorderSupported(recorder);
      if (boolResult)
      {
        Debug.WriteLine("--- Current recorder IS supported. ---");
      }
      else
      {
        Debug.WriteLine("--- Current recorder IS NOT supported. ---");
      }
      boolResult = mediaImage.IsCurrentMediaSupported(recorder);
      if (boolResult)
      {
        Debug.WriteLine("--- Current media IS supported. ---");
      }
      else
      {
        Debug.WriteLine("--- Current media IS NOT supported. ---");
      }
      Debug.WriteLine("ClientName = {0}", mediaImage.ClientName);
      uint curMediaStatus = (uint) mediaImage.CurrentMediaStatus;
      Debug.WriteLine("Checking Current Media Status");
      if (curMediaStatus == (uint) IMAPI_FORMAT2_DATA_MEDIA_STATE.IMAPI_FORMAT2_DATA_MEDIA_STATE_UNKNOWN)
      {
        Debug.WriteLine("\tMedia state is unknown.");
      }
      else
      {
        if ((curMediaStatus & (uint) IMAPI_FORMAT2_DATA_MEDIA_STATE.IMAPI_FORMAT2_DATA_MEDIA_STATE_OVERWRITE_ONLY) != 0)
        {
          Debug.WriteLine("\tCurrently, only overwriting is supported.");
        }
        if ((curMediaStatus & (uint) IMAPI_FORMAT2_DATA_MEDIA_STATE.IMAPI_FORMAT2_DATA_MEDIA_STATE_RANDOMLY_WRITABLE) !=
            0)
        {
          Debug.WriteLine("\tCurrently, media supports random writing.");
        }
        if ((curMediaStatus & (uint) IMAPI_FORMAT2_DATA_MEDIA_STATE.IMAPI_FORMAT2_DATA_MEDIA_STATE_APPENDABLE) != 0)
        {
          Debug.WriteLine("\tMedia is currently appendable.");
        }
        if ((curMediaStatus & (uint) IMAPI_FORMAT2_DATA_MEDIA_STATE.IMAPI_FORMAT2_DATA_MEDIA_STATE_FINAL_SESSION) != 0)
        {
          Debug.WriteLine("\tMedia is in final writing session.");
        }
        if ((curMediaStatus & (uint) IMAPI_FORMAT2_DATA_MEDIA_STATE.IMAPI_FORMAT2_DATA_MEDIA_STATE_DAMAGED) != 0)
        {
          Debug.WriteLine("\tMedia is damaged.");
        }
      }
      IMAPI_MEDIA_PHYSICAL_TYPE mediaType = mediaImage.CurrentPhysicalMediaType;
      Console.Write("Current Media Type");
      DisplayMediaType(mediaType);
      Debug.WriteLine("SupportedMediaTypes in the device: ");
      foreach (IMAPI_MEDIA_PHYSICAL_TYPE supportedMediaType in mediaImage.SupportedMediaTypes)
      {
        DisplayMediaType(supportedMediaType);
      }
      Console.Write("\n----- Finished -----");
    }

    /// <summary>
    /// Helper Function: Displays the string of the specified IMAPI_MEDIA_PHYSICAL_TYPE
    /// </summary>
    /// <param name="mediaType">The IMAPI_MEDIA_PHYSICAL_TYPE to display</param>
    private void DisplayMediaType(IMAPI_MEDIA_PHYSICAL_TYPE mediaType)
    {
      switch (mediaType)
      {
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_UNKNOWN:
          {
            Debug.WriteLine("\tEmpty device or an unknown disc type.");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDROM:
          {
            Debug.WriteLine("\tCD-ROM");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDR:
          {
            Debug.WriteLine("\tCD-R");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDRW:
          {
            Debug.WriteLine("\tCD-RW");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDROM:
          {
            Debug.WriteLine("\tRead-only DVD drive and/or disc");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDRAM:
          {
            Debug.WriteLine("\tDVD-RAM");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR:
          {
            Debug.WriteLine("\tDVD+R");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW:
          {
            Debug.WriteLine("\tDVD+RW");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR_DUALLAYER:
          {
            Debug.WriteLine("\tDVD+R Dual Layer media");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR:
          {
            Debug.WriteLine("\tDVD-R");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHRW:
          {
            Debug.WriteLine("\tDVD-RW");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR_DUALLAYER:
          {
            Debug.WriteLine("\tDVD-R Dual Layer media");
          }
          break;
        case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK:
          {
            Debug.WriteLine("\tRandomly-writable, hardware-defect managed media type" +
                            " that reports the \"Disc\" profile as current.");
          }
          break;
        default:
          {
            Debug.WriteLine("Error!: MediaPhysicalType");
          }
          break;
      }
    }

    public void DiscFormat2Data_ProgressUpdate([In, MarshalAs(UnmanagedType.IDispatch)] object sender,
                                               [In, MarshalAs(UnmanagedType.IDispatch)] object args)
    {
      IDiscFormat2DataEventArgs progress = args as IDiscFormat2DataEventArgs;
      if (progress != null)
      {
        String timeStatus = String.Format("Time: {0} / {1} ({2})",
                                          progress.ElapsedTime,
                                          progress.TotalTime,
                                          progress.ElapsedTime/progress.TotalTime);
        switch (progress.CurrentAction)
        {
          case IMAPI_FORMAT2_DATA_WRITE_ACTION.IMAPI_FORMAT2_DATA_WRITE_ACTION_VALIDATING_MEDIA:
            {
              Console.Write("Validating media. ");
            }
            break;
          case IMAPI_FORMAT2_DATA_WRITE_ACTION.IMAPI_FORMAT2_DATA_WRITE_ACTION_FORMATTING_MEDIA:
            {
              Console.Write("Formatting media. ");
            }
            break;
          case IMAPI_FORMAT2_DATA_WRITE_ACTION.IMAPI_FORMAT2_DATA_WRITE_ACTION_INITIALIZING_HARDWARE:
            {
              Console.Write("Initializing Hardware. ");
            }
            break;
          case IMAPI_FORMAT2_DATA_WRITE_ACTION.IMAPI_FORMAT2_DATA_WRITE_ACTION_CALIBRATING_POWER:
            {
              Console.Write("Calibrating Power (OPC). ");
            }
            break;
          case IMAPI_FORMAT2_DATA_WRITE_ACTION.IMAPI_FORMAT2_DATA_WRITE_ACTION_WRITING_DATA:
            {
              double totalSectors = (double) progress.SectorCount;
              double writtenSectors = (double) progress.LastWrittenLba - progress.StartLba;
              double percentDone = (writtenSectors / totalSectors)*100;
              Console.Write("Progress: {0} - ", Convert.ToInt32(percentDone));
            }
            break;
          case IMAPI_FORMAT2_DATA_WRITE_ACTION.IMAPI_FORMAT2_DATA_WRITE_ACTION_FINALIZATION:
            {
              Console.Write("Finishing the writing. ");
            }
            break;
          case IMAPI_FORMAT2_DATA_WRITE_ACTION.IMAPI_FORMAT2_DATA_WRITE_ACTION_COMPLETED:
            {
              Console.Write("Completed the burn.");
            }
            break;
          default:
            {
              Console.Write("Error!!!! Unknown Action: 0x{0:X}", progress.CurrentAction);
            }
            break;
        }
        Debug.WriteLine(timeStatus);
      }
    }

    private string _folder2Burn;

    private void btnOpenFolder_Click(object sender, EventArgs e)
    {
      FolderBrowserDialog dialog = new FolderBrowserDialog();
      dialog.ShowDialog(this);
      _folder2Burn = dialog.SelectedPath;
    }

    private delegate void BurnDirectoryDelegate(IDiscRecorder2 recorder, string path);
    private void btnBurn_Click(object sender, EventArgs e)
    {
      int index = 0;
      IDiscMaster2 discMaster = new MsftDiscMaster2();
      IDiscRecorder2 recorder = new MsftDiscRecorder2();
      recorder.InitializeDiscRecorder(discMaster[index]);
      //BurnDirectory(recorder, _folder2Burn);
      BurnDirectoryDelegate bdd = new BurnDirectoryDelegate(BurnDirectory);
      IAsyncResult asyncResult = bdd.BeginInvoke(recorder, _folder2Burn, null, null);
      while(!asyncResult.IsCompleted)
      {
        Thread.Sleep(1);
        Application.DoEvents();
      }

      bdd.EndInvoke(asyncResult);
      bdd = null;
    }

      private void Form1_Load(object sender, EventArgs e)
      {

      }

    private void button1_Click(object sender, EventArgs e)
    {
      int index = 0;
      IDiscMaster2 discMaster = new MsftDiscMaster2();
      IDiscRecorder2 recorder = new MsftDiscRecorder2();
      recorder.InitializeDiscRecorder(discMaster[index]);
      DisplayMediaInformation(recorder);
      DisplayRecorderInformation(recorder);
    }
  }
}